package com.example.mybatislearn.my;

import com.github.pagehelper.Page;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import java.util.*;

@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)

public class PageHelper implements Interceptor {
    public static ThreadLocal<Pageable> pageInfos = new ThreadLocal<>();

    public static void startPage(int pageNum,int pageSize) {
        pageInfos.set(PageRequest.of(pageNum,pageSize));
    }

    public static Pageable getPage() {
        return pageInfos.get();
    }

    public static void remove() {
        pageInfos.remove();
    }


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {

            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            SqlSource sqlSource = mappedStatement.getSqlSource();
            BoundSql boundSql;
            if (invocation.getArgs().length == 6) {
                boundSql = (BoundSql) invocation.getArgs()[5];
            } else {
                boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
            }

            Object parameterObject = invocation.getArgs()[1];
            //判读是否需要分页,判断的逻辑就是PageHelper 中是否有page信息
            Pageable page = PageHelper.getPage();
            if (page != null) {
                //查询总的total
                String sql = boundSql.getSql();
                String prefix = "Select count(0) ";
                int from = sql.indexOf("from");
                String countSql = prefix + sql.substring(from, sql.length());
                Executor executor = (Executor) invocation.getTarget();

                ResultMap resultMap = new ResultMap.Builder(mappedStatement.getConfiguration(), mappedStatement.getId() + "count", Integer.class, Collections.EMPTY_LIST, true).build();

                StaticSqlSource countBoundSql = new StaticSqlSource(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings());

                MappedStatement countMappedStatement = new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId() + "count", countBoundSql, mappedStatement.getSqlCommandType())
                        .cache(mappedStatement.getCache())
                        .databaseId(mappedStatement.getDatabaseId())
                        .fetchSize(mappedStatement.getFetchSize())
                        .statementType(mappedStatement.getStatementType())
                        .resultMaps(Arrays.asList(resultMap)).build();

                List<Object> query = executor.query(countMappedStatement,parameterObject,(RowBounds)invocation.getArgs()[2],(ResultHandler)invocation.getArgs()[3]);
                int total = (int) query.get(0);
                //分页插叙
                int pageSize = page.getPageSize();
                int pageNumber = page.getPageNumber();

                String selectSql = boundSql.getSql();
                selectSql = selectSql + " " + "limit " + (pageNumber - 1) * pageSize + "," + pageSize;

                BoundSql selectBoundSql = new BoundSql(mappedStatement.getConfiguration(), selectSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
                //进行查询操作
                Object proceed = executor.query(mappedStatement,parameterObject,(RowBounds)invocation.getArgs()[2],(ResultHandler)invocation.getArgs()[3],(CacheKey) invocation.getArgs()[4],selectBoundSql);
                Page ret = new Page(pageNumber, pageSize);
                ret.setTotal(total);
                ret.addAll((Collection) proceed);
                return ret;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            //清除分页信息
            PageHelper.remove();
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
